﻿using System;
using System.IO;
using System.Collections.Generic;
using NAnt.Core;
using NAnt.Core.Attributes;
using NAnt.Core.Types;
using System.Threading;

namespace NAntExt.Tasks
{
    [TaskName("parallel-begin")]
    public class BeginParallelTask : TaskContainer
    {

        protected readonly Guid ParallelThreadId = Guid.NewGuid();
        protected ParallelTask ThreadContext;

        [TaskAttribute("contextKey")]
        [StringValidator(AllowEmpty = false)]
        public string ContextKey { get; set; }

        [TaskAttribute("description")]
        [StringValidator(AllowEmpty = true)]
        public string Description { get; set; }

        [TaskAttribute("propertyNames")]
        [StringValidator(AllowEmpty = false)]
        public string PropertyNames { get; set; }

        [BuildElement("do")]
        public TaskContainer ChildTasks { get; set; }

        /// <summary>
        /// Entry point for the task.
        /// </summary>
        protected override void ExecuteTask()
        {
            try
            {
                if (string.IsNullOrEmpty(ContextKey))
                {
                    ContextKey = ParallelContext.DefaultContextKey;
                }

                if (string.IsNullOrEmpty(Name))
                {
                    Description = "(default)";
                }

                // create the context associated with the thread
                ParallelContext context = ParallelContext.GetContext(this.ContextKey);
                if (context == null)
                {
                    context = new ParallelContext();
                    ParallelContext.Contexts[this.ContextKey] = context;
                }

                // spawn a seperate thread for the actual work
                Thread paralellThread = new Thread(BeginExecuteTask);
                paralellThread.Name = string.Format("parallel_{0}", Guid.NewGuid().ToString().Trim('{', '}'));
                this.ThreadContext = new ParallelTask(ParallelThreadId, paralellThread, Description);
                context.Enqueue(this.ThreadContext);

                // set the properties for the thread
                if (!string.IsNullOrEmpty(PropertyNames))
                {
                    foreach (string propertyName in PropertyNames.Split(','))
                    {
                        Functions.ParallelFunctions.SetThreadPropertyValue(paralellThread, propertyName, this.Properties[propertyName]);
                    }
                }

                paralellThread.Start();
            }
            catch (Exception ex)
            {
                this.ThrowOrLog(this.Location, ex);
            }
        }

        private void BeginExecuteTask(object state)
        {
            try
            {
                this.ThreadContext.Timer.Start();

                if (ChildTasks == null)
                {
                    base.ExecuteTask();
                }
                else
                {
                    ChildTasks.Execute();
                }
            }
            catch (Exception ex)
            {
                if (this.Verbose)
                {
                    Log(Level.Error, "Error: {0}", ex);
                }
                else
                {
                    Log(Level.Error, "Error: {0}", ex.Message);
                }

                try
                {
                    this.ThreadContext.Exceptions.Enqueue(ex);
                }
                catch (Exception exInner)
                {
                    Log(Level.Error, "Error: {0}", exInner);
                }
            }
            finally
            {
                // stop timer
                this.ThreadContext.Timer.Stop();

                // signle completed
                this.ThreadContext.CompletedEvent.Set();                
            }
        }

    }
}
